﻿using System;
using System.Threading.Tasks;
using Red.Controls.Shell;
using Red.Launcher.Utilities;
using static System.Windows.Application;
using P4 = Red.Core.Perforce;

namespace Red.Launcher.ViewModels.Workspaces
{
    public class SpecificationWorkspace : Workspace
    {
        #region Types

        protected class MediatorErrorHandler : IDisposable
        {
            #region Properties

            public P4.Mediator Mediator { get; private set; }

            #endregion Properties

            #region C-Tor

            public MediatorErrorHandler( P4.Mediator mediator )
            {
                Mediator = mediator;

                Mediator.Error += Mediator_Error;
            }

            #endregion C-Tor

            #region Event Handlers

            private void Mediator_Error( string message )
            {
                RedMessageBox.ShowAsync( Current.MainWindow, RedDialogType.Ok, $"There was a problem updating perforce, the following error was returned:\n\n{message}", "Perforce Error", RedMessageBox.Mode.Warning );
            }

            #endregion Event Handlers

            #region IDisposable

            public void Dispose()
            {
                Mediator.Error -= Mediator_Error;
                Mediator = null;
            }

            #endregion IDisposable
        }

        #endregion Types

        #region Public Properties

        public int Revision { get; set; }
        public Database.User User { get; set; }

        #endregion Public Properties

        #region Implemented Properties

        public override string Uid => $"SpecificationWorkspace.{WorkspaceInfo.Id}.{Revision}";
        public override bool IsInstalled => false;
        public override string IsInstalledDescription => "Not Installed";
        public override string PerforceName => $"{( (Database.P4ClientSpecification)WorkspaceInfo ).ClientName}_{Environment.MachineName}_{User.UserPrincipal.SamAccountName}";
        public override string PerforceNameShort => ( (Database.P4ClientSpecification)WorkspaceInfo ).ClientName;

        #endregion Implemented Properties

        #region Workspace

        public async override Task<bool> CreateInPerforce( string location )
        {
            // Make sure we've got a valid P4 mediator that's signed in and ready for use
            P4.Mediator mediator = await CreateMediator();

            // Nope, didn't work, back out
            if ( mediator == null )
                return false;

            // This error handler IDisposable will automatically de-register the error handler, so it won't leak
            using ( MediatorErrorHandler errorHandler = new MediatorErrorHandler( mediator ) )
            {
                Database.P4ClientSpecification specification = (Database.P4ClientSpecification)WorkspaceInfo;

                string stream = CreateStream( mediator, specification );

                if ( stream == null )
                    return false;

                if ( !CreateWorkspace( mediator, stream, specification, location ) )
                    return false;

                if ( !await RegisterWorkspace( specification ) )
                {
                    // For whatever reason, we couldn't register the workspace in the
                    // database, so let's just undo the workspace creation itself
                    DestroyWorkspace( mediator );

                    return false;
                }
            }

            return true;
        }

        #endregion Workspace

        #region Protected Methods

        /// <summary>
        /// Creates a new p4 object, ensures the user is signed in
        /// </summary>
        /// <returns></returns>
        protected async Task< P4.Mediator > CreateMediator()
        {
            P4.Mediator mediator = new P4.Mediator( Settings.PerforceServer, Credentials.Name, null );
            if ( !await mediator.SignInCheckAndPrompt() )
            {
                return null;
            }

            return mediator;
        }

        protected P4.StreamBuilder CreateStreamBuilder( Database.P4ClientSpecification specification )
        {
            string formattedFilter = specification.StreamFilter.Replace( "{username}", User.UserPrincipal.SamAccountName );

            P4.StreamBuilder builder = new P4.StreamBuilder();

            builder.Id = $"{User.UserPrincipal.SamAccountName}.{Environment.MachineName}.{specification.ClientName}";
            builder.Parent = specification.Stream;
            builder.Name = $"{specification.Name} instance for {User.UserPrincipal.DisplayName}";
            builder.LockContentsToOwner = true;
            builder.Type = P4.StreamType.Virtual;
            builder.AddPathSpecification( formattedFilter );

            return builder;
        }

        protected string CreateStream( P4.Mediator mediator, Database.P4ClientSpecification specification )
        {
            if ( specification.StreamFilter == null )
                return specification.Stream;

            P4.StreamBuilder builder = CreateStreamBuilder( specification );

            string stream = mediator.CreateStream( builder );

            return stream;
        }

        #endregion Protected Methods

        #region Private Methods

        /// <summary>
        /// Create the workspace in perforce based on the specification
        /// </summary>
        private bool CreateWorkspace( P4.Mediator mediator, string stream, Database.P4ClientSpecification specification, string location )
        {
            return mediator.CreateWorkspace
            (
                PerforceName,
                location,
                stream,
                specification.OptionOnSubmit,
                specification.OptionLineEndings,
                specification.OptionAllWrite,
                specification.OptionClobber,
                specification.OptionCompress,
                specification.OptionModTime,
                specification.OptionRMDir
            );
        }

        /// <summary>
        /// Store the information about this workspace in the database
        /// </summary>
        private async Task< bool > RegisterWorkspace( Database.P4ClientSpecification specification )
        {
            string sql = $@"INSERT INTO [{Settings.DatabaseName}].[dbo].[ImplementedClientSpecs] ([users_id], [p4credentials_id], [p4specs_id], [revision], [host], [client_name])
                                    VALUES ({User.Id}, {Credentials.Id}, {specification.Id}, {Revision}, '{Environment.MachineName}', '{PerforceName}')";

            DatabaseHelper.Result result = await DatabaseHelper.ExecuteAsyncSafe( sql, Settings.DatabaseConnectionString );

            return result == DatabaseHelper.Result.Success;
        }

        private bool DestroyWorkspace( P4.Mediator mediator )
        {
            return mediator.DeleteWorkspace( PerforceName );
        }

        #endregion Private Methods
    }
}
